﻿//*****************************************************************************
// Copyright (C) 2013 Cognex Corporation
//
// Subject to Cognex Corporation's terms and conditions and license
// agreement, you are authorized to use and modify this source code in
// any way you find useful, provided the Software and/or the modified
// Software is used solely in conjunction with a Cognex Machine Vision
// System.  Furthermore you acknowledge and agree that Cognex has no
// warranty, obligations or liability for your use of the Software.
//*****************************************************************************
// This sample program is designed to illustrate certain VisionPro
// features or techniques in the simplest way possible. It is not
// intended as the framework for a complete application. In particular,
// the sample program may not provide proper error handling, event
// handling, cleanup, repeatability, and other mechanisms that a
// commercial quality application requires.
//
// This program assumes that you have some knowledge of C# and VisionPro
// programming.
//
// This sample demonstrates how to modify properties, and acquire
// Intensity and Range images using the Profile Camera.
//

using System;
using Cognex.VisionPro;
using Cognex.VisionPro.Exceptions;

namespace ProfileCameraAcquisition
{
    /// <summary>
    /// Helper class wrapping the Profile Camera AcqFifo. It provides additional functionality
    /// to interact with the camera. This class is not thread-safe.
    /// </summary>
    public class ProfileCameraHelper : IDisposable
    {
        public ICogAcqFifo ProfileCamFifo;

        /// <summary>
        /// Event raised when acquisition finishes
        /// </summary>
        public delegate void ImageReadyEventHandler(object sender, EventArgs e, ICogImage img);
        public event ImageReadyEventHandler ImageReady;
        
        /// <summary>
        /// Event raised when asyncronous acq error occurs
        /// </summary>
        public delegate void AsynchronousAcqErrorEventHandler(object sender, string errorMsg);
        public event AsynchronousAcqErrorEventHandler AsynchronousAcqError;

        // Used to detect redundant Dispose() calls
        private bool mDisposed = false;

        // Prevents the event handler from running if synchronous acquisition is in progress
        private bool mSyncronousAcquire = false;


        /// <summary>
        /// Constructs a ProfileCameraHelper wrapper around the P+ fifo
        /// </summary>
        /// <param name="fifo">Profile Camera fifo</param>
        public ProfileCameraHelper(ICogAcqFifo fifo)
        {
            ProfileCamFifo = fifo;
            ProfileCamFifo.Complete += this.ProfileCameraFifo_Complete;
        }

        /// <summary>
        /// Use this method to acquire an image from the FIFO, preventing the subscribed
        /// event handler to go off, and blocking the caller while the acquisition is being done
        /// Throws any exception occured during the acquisition.
        /// </summary>
        /// <returns>Acquired image if acq successful, or null</returns>
        public ICogImage SyncAcquire()
        {
            // Throw exception if object already disposed
            if (mDisposed)
            {
                throw new ObjectDisposedException("ProfileCameraHelper");
            }

            // Set this flag to true, so the event handler won't try to finish the acqusition
            this.mSyncronousAcquire = true;

            try
            {
                int triggerNo;
                
                // Syncronous acquisition
                ICogImage retImg = ProfileCamFifo.Acquire(out triggerNo);
                
                // Raise ready event
                ImageReady(this, EventArgs.Empty, retImg);

                return retImg;
            }
            catch (Exception)
            {
                // Syncronous acq, throw the exception further
                throw;
            }
            finally
            {
                // Flush queued acquisitions
                this.ProfileCamFifo.Flush();

                // Make sure to reset the flag
                this.mSyncronousAcquire = false;
            }
        }

        /// <summary>
        /// Regular dispose implementation
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        public void Dispose(bool disposing)
        {
            if (!mDisposed)
            {
                if (disposing)
                {
                    // Unsubscribe from complete event, disconnect GigE camera
                    ProfileCamFifo.Complete -= this.ProfileCameraFifo_Complete;
                    ProfileCamFifo.FrameGrabber.Disconnect(false);
                }

                mDisposed = true;
            }
        }

        /// <summary>
        /// Event handler for completing the asyncronous acquisitions using the P+ camera of the ProfileCameraHelper object.
        /// It won't run when acquiring syncronously using the SyncAcquire method (mSyncronousAcquire == true)
        /// </summary>
        private void ProfileCameraFifo_Complete(object sender, CogCompleteEventArgs e)
        {
            // Throw exception if object already disposed
            if (mDisposed)
            {
                throw new ObjectDisposedException("ProfileCameraHelper");
            }

            // Prevent running if synchronous acquisition was started (Acquire, not StartAcquire)
            if (mSyncronousAcquire)
            {
                return;
            }

            // Query fifo state
            int numPending = 0;
            int numReady = 0;
            bool busy = false;
            ProfileCamFifo.GetFifoState(out numPending, out numReady, out busy);

            // Complete acquisition if ready
            if (numReady > 0)
            {
                ICogImage img = null;
                try
                {
                    int ticket = 0;
                    int triggerNum = 0;
                    img = ProfileCamFifo.CompleteAcquire(-1, out ticket, out triggerNum);
                }
                catch (CogAcqEncoderOverrunException)
                {
                    // Encoder is too fast!
                    AsynchronousAcqError(this, "Encoder overrun occured!");
                    return;
                }
                catch (CogAcqTimeoutException)
                {
                    // Encoder is too slow or not detected!
                    // You can increase the acq timeout as well.
                    AsynchronousAcqError(this, "Acquisition timeout elapsed.");
                    return;
                }
                catch (Exception ex)
                {
                    // Another error occured!
                    AsynchronousAcqError(this, "Acquisition error: " + ex.Message);
                    return;
                }

                // Raise image ready event!
                ImageReady(this, EventArgs.Empty, img);
            }
        }
    }
}
